#include "CommProtocol.hpp"
#include "stdio.h"
#include "crc16.hpp"

static const uint16_t __magic_number = 0xaabb;
static const uint16_t comm_protocol_resp_ok = 1;
static const uint16_t comm_protocol_resp_failed = 0;
static const int comm_protocol_heartbeats_interval = 20;


static CommProtocolPortable_t __portable;
static int __certified = 0;
static uint16_t __auth_key = 0x0000;
static int __heartbeat_remain = 0x05;

void CommProtocol_init(CommProtocolPortable_t portable){
    __portable = portable;
    __certified = 0;
}

typedef struct CommProtocolAuthRequestContent {
    uint8_t password[8];
}CommProtocolAuthRequestContent_t;

typedef struct CommProtocolAuthResponseContent {
    uint16_t status;
    uint16_t auth_key;
}CommProtocolAuthResponseContent_t;

static void CommProtocol_auth(char *content,int size){
    char response_buffer[sizeof(CommProtocolResponseHeader_t) + sizeof(CommProtocolAuthResponseContent_t)];
    if ( size < sizeof(CommProtocolAuthRequestContent_t) ) return;
    CommProtocolAuthRequestContent_t *auth_content = (CommProtocolAuthRequestContent_t*)content;
    CommProtocolResponseHeader_t *response_header = (CommProtocolResponseHeader_t*)response_buffer;
    response_header->magic_number = __magic_number;
    response_header->type = ResponseType_Auth;
    response_header->length = sizeof(CommProtocolAuthResponseContent_t);
    CommProtocolAuthResponseContent_t *response_content = (CommProtocolAuthResponseContent_t*)(response_buffer +sizeof(CommProtocolResponseHeader_t) );
    if ( __portable.authenticate((char*)auth_content->password) ){
        /* Authenticate pass. */
        __auth_key = __portable.randKey();
        response_content->status = comm_protocol_resp_ok;
        response_content->auth_key = __auth_key;
        __certified = 1;
        __heartbeat_remain = comm_protocol_heartbeats_interval;
    }else{
       // response_content->status = ota_response_failed;
    }
    
    response_header->crc16 = crc16(response_content,sizeof(CommProtocolAuthResponseContent_t));
    __portable.sendResponse(response_buffer,sizeof(CommProtocolResponseHeader_t) + sizeof(CommProtocolAuthResponseContent_t));
}

static void CommProtocol_heartBeat(char *content,int size){
    //printf("heart beat\r\n");
    __heartbeat_remain = comm_protocol_heartbeats_interval;
}



typedef struct CommProtocolReadResponseContent {
    int index;
}CommProtocolReadResponseContent_t;

static void CommProtocol_read(char *content,int size){
    if ( size < sizeof(CommProtocolReadRequestContent_t) ) return;
    CommProtocolReadRequestContent_t *read_content = (CommProtocolReadRequestContent_t*)content;
    char response_buffer[512];
    CommProtocolResponseHeader_t *response_header = (CommProtocolResponseHeader_t*)response_buffer;
    CommProtocolReadResponseContent_t *response_content = (CommProtocolReadResponseContent_t*)(response_buffer + sizeof(CommProtocolResponseHeader_t));
    int read_size;
    __portable.readData(read_content->index,response_buffer + sizeof(CommProtocolResponseHeader_t) + sizeof(CommProtocolReadResponseContent_t),&read_size);
    response_header->magic_number = __magic_number;
    response_header->type = ResponseType_Read;
    response_header->length = read_size + sizeof(read_content->index);
    response_header->reserved = 0xABCD;
    response_content->index = read_content->index;
    response_header->crc16 = crc16(response_buffer + sizeof(CommProtocolResponseHeader_t),response_header->length);

    __portable.sendResponse(response_buffer,sizeof(CommProtocolResponseHeader_t) + read_size + sizeof(CommProtocolReadResponseContent_t));
}

typedef struct CommProtocolWriteRequestContent {
    int index;
}CommProtocolWriteRequestContent_t;

typedef struct CommProtocolWriteResponseContent {
    int index;
}CommProtocolWriteResponseContent_t;

static void CommProtocol_write(char *content,int size){
    // printf("write\r\n");
    CommProtocolWriteRequestContent_t *write_content = (CommProtocolWriteRequestContent_t*)content;
    char response_buffer[64];
    CommProtocolResponseHeader_t *response_header = (CommProtocolResponseHeader_t*)response_buffer;
    CommProtocolWriteResponseContent_t *response_content = (CommProtocolWriteResponseContent_t*)(response_buffer + sizeof(CommProtocolResponseHeader_t));
    __portable.writeData(write_content->index,content + sizeof(CommProtocolWriteRequestContent_t),size - sizeof(CommProtocolWriteRequestContent_t));
    response_header->magic_number = __magic_number;
    response_header->type = ResponseType_Write;
    response_header->length = sizeof(CommProtocolWriteResponseContent_t);
    response_header->reserved = 0xABCD;
    response_content->index = write_content->index;
    response_header->crc16 = crc16(response_content,sizeof(CommProtocolWriteResponseContent_t));
    
    __portable.sendResponse(response_buffer,sizeof(CommProtocolResponseHeader_t) + sizeof(CommProtocolWriteResponseContent_t));
}

typedef struct CommProtocolOTARequestContent {
    uint8_t type;
}CommProtocolOTARequestContent_t;

typedef struct CommProtocolOTAResponseContent {
    uint8_t status;
}CommProtocolOTAResponseContent_t;


typedef struct StartOTA_Request {
    uint8_t ota_type;
    unsigned int file_size;
    unsigned int packet_size;
    unsigned int packets_number;
}__attribute__((packed)) StartOTA_Request_t;

typedef struct StartOTA_Response {
    uint8_t ota_type;
    uint8_t status;
}__attribute__((packed)) StartOTA_Response_t;

typedef struct WriteOTA_Request {
    uint8_t ota_type;
    unsigned int current_packets;
}__attribute__((packed)) WriteOTA_Request_t;

typedef struct WriteOTA_Response {
    uint8_t ota_type;
    uint8_t status;
    uint32_t current_packets;
}__attribute__((packed)) WriteOTA_Response_t;

typedef struct StopOTA_Request {
    uint8_t ota_type;
}StopOTA_Request_t;

typedef struct StopOTA_Response {
    uint8_t ota_type;
    uint8_t status;
}StopOTA_Response_t;

static void CommProtocol_OTA(char *content,int size){
    StartOTA_Request_t *startota_request = NULL;
    StartOTA_Response_t *startota_response = NULL;

    WriteOTA_Request_t *write_request = NULL;
    WriteOTA_Response_t *writeota_response = NULL;
    
    StopOTA_Request_t *stopota_request = NULL;
    StopOTA_Response_t *stopota_response = NULL;

    static bool ota_start_flag = false;
    static uint32_t ota_address_offset = 0x00000000;
    char response_buffer[64];

    static uint32_t packets_count;
    static uint32_t current_packet_cnt = 0;

    CommProtocolOTARequestContent_t *ota_content = (CommProtocolOTARequestContent_t*)content;
    CommProtocolResponseHeader_t *response_header = (CommProtocolResponseHeader_t*)response_buffer;

    switch( ota_content->type){
        case comm_protocol_ota_type_start:

            /* 开始OTA类型  解析数据*/
            startota_request = (StartOTA_Request_t*)ota_content;
            packets_count = startota_request->packets_number;

            /* 确定应答的地址 除去固定帧地址 */
            startota_response = (StartOTA_Response_t*)(response_buffer + sizeof(CommProtocolResponseHeader_t));
            startota_response->ota_type = comm_protocol_ota_type_start;
            /* 判断文件大小是否合法 当前是200Kb */
            if( startota_request->file_size > 200 * 1024){
                startota_response->status = ota_start_filesize_error;
                ota_start_flag = false;               
            }
            /* 判断切片大小是否合法*/
            else if( startota_request->packet_size > 450 || startota_request->packet_size %2 != 0){
                startota_response->status = ota_start_packetsize_error;
                ota_start_flag = false;                   
            }
            /* 判断切片数量是否合法 */
            else if(startota_request->packets_number != (startota_request->file_size + startota_request->packet_size - 1) / startota_request->packet_size ){
                startota_response->status = ota_start_packetsnumber_error;
                ota_start_flag = false;   
            }
            else{
                startota_response->status = ota_response_OK;
                ota_start_flag = true;
                printf("file_size %d \r\n",startota_request->file_size);
                printf("packets_count %d \r\n",startota_request->packets_number);
                __portable.startOTA( startota_request->file_size );
            }
            /* 处理固定的帧格式 */
            response_header->magic_number = __magic_number;
            response_header->type = ResponseType_OTA;
            response_header->reserved = 0xABCD;
            response_header->length = sizeof(StartOTA_Response_t);
            response_header->crc16 = crc16(startota_response,sizeof(StartOTA_Response_t));
            /* 发送应答 */
            __portable.sendResponse(response_buffer,sizeof(CommProtocolResponseHeader_t) + sizeof(StartOTA_Response_t));
            break;
        case comm_protocol_ota_type_data:
            /* OTA数据更新  */
            write_request = (WriteOTA_Request_t*)ota_content;
            writeota_response = (WriteOTA_Response_t*)(response_buffer + sizeof(CommProtocolResponseHeader_t));

            /* 判断是否有开启OTA信号 */
            if( ota_start_flag == false){
                writeota_response->status = ota_write_nostart_error;     
            }else{
                /* 如果有开启OTA信号 判断当前数据包索引是否正确 */
                if( write_request->current_packets == current_packet_cnt){
                    __portable.writeOTAData(ota_address_offset,content + sizeof(WriteOTA_Request_t),size - sizeof(WriteOTA_Request_t));
                    ota_address_offset += size - sizeof(WriteOTA_Request_t);
                    writeota_response->status = ota_response_OK;
                }else{
                    // printf("        write_request->current_packets %d\r\n",write_request->current_packets);
                    // printf("        current_packet_cnt %d\r\n",current_packet_cnt);
                    // printf("        packets error\r\n");
                    writeota_response->status = ota_write_packetindex_error;               
                }
            }
            /* 应答数据填充 */
            writeota_response->ota_type = comm_protocol_ota_type_data;
            writeota_response->current_packets = current_packet_cnt ;
            /* 只有索引正确的时候 索引才累加*/
            if( write_request->current_packets == current_packet_cnt ){
                current_packet_cnt += 1;            
            }
            /* 处理固定帧格式 */
            response_header->magic_number = __magic_number;
            response_header->type = ResponseType_OTA;
            response_header->length = sizeof(WriteOTA_Response_t);
            response_header->reserved = 0xABCD;
            response_header->crc16 = crc16(writeota_response,sizeof(WriteOTA_Response_t));

            __portable.sendResponse(response_buffer,sizeof(CommProtocolResponseHeader_t) + sizeof(WriteOTA_Response_t));
            break;
        case comm_protocol_ota_type_stop:
            stopota_request = (StopOTA_Request_t*)ota_content;
            stopota_response = (StopOTA_Response_t*)(response_buffer + sizeof(CommProtocolResponseHeader_t));

            response_header->magic_number = __magic_number;
            response_header->type = ResponseType_OTA;
            response_header->length = sizeof(StopOTA_Response_t);
            response_header->reserved = 0xABCD;
            stopota_response->ota_type = comm_protocol_ota_type_stop;
            
            /* 校验数据 */
            if( packets_count == current_packet_cnt ){
                stopota_response->status = ota_response_OK;
                response_header->crc16 = crc16(stopota_response,sizeof(StopOTA_Response_t));
                __portable.sendResponse(response_buffer,sizeof(CommProtocolResponseHeader_t) + sizeof(StopOTA_Response_t));
                __portable.verifyOTA();
                
            }else{
                // printf("        verify error\r\n");
                // printf("        packets_count %x\r\n",packets_count);
                // printf("        current_packet_cnt %x\r\n",current_packet_cnt);
                stopota_response->status = ota_verify_error;
                response_header->crc16 = crc16(stopota_response,sizeof(StopOTA_Response_t));
                __portable.sendResponse(response_buffer,sizeof(CommProtocolResponseHeader_t) + sizeof(StopOTA_Response_t));
            }
            ota_start_flag = false;
            current_packet_cnt = 0;
            ota_address_offset = 0x00000000;
            break;
        default:
            response_header->magic_number = __magic_number;
            response_header->type = ResponseType_OTA;
            response_header->length = 0xFFFFFFFF;
            response_header->reserved = 0xFFFF;
            response_header->crc16 = 0xFFFF;
            __portable.sendResponse(response_buffer,sizeof(CommProtocolResponseHeader_t));
            break;
    }

    //__portable.sendResponse(response_buffer,sizeof(CommProtocolResponseHeader_t) + sizeof(CommProtocolOTAResponseContent_t));


    // printf("here\r\n");
    // static bool ota_start_flag = false;
    // static uint32_t ota_address_offset = 0x00000000;
    // char response_buffer[64];
    // CommProtocolOTARequestContent_t *ota_content = (CommProtocolOTARequestContent_t*)content;
    // CommProtocolResponseHeader_t *response_header = (CommProtocolResponseHeader_t*)response_buffer;
    // CommProtocolOTAResponseContent_t *response_content = (CommProtocolOTAResponseContent_t*)(response_buffer + sizeof(CommProtocolResponseHeader_t));
    // response_header->magic_number = __magic_number;
    // response_header->type = ResponseType_OTA;
    // response_header->length = sizeof(CommProtocolOTAResponseContent_t);
    // response_header->reserved = 0xABCD;
    // response_header->crc16 = crc16(response_content,sizeof(CommProtocolOTAResponseContent_t));
    // //printf("data%d\r\n",ota_content->type);
    // switch ( ota_content->type ){
    //     case comm_protocol_ota_type_start: 
    //         ota_start_flag = true;
    //         ota_address_offset = 0x00000000;
    //         response_content->status = ota_start_finished;
    //         break;
    //     case comm_protocol_ota_type_data:
    //         if( ota_start_flag == false ){
                
    //             response_content->status = ota_response_failed;
    //             break;                
    //         }
    //         __portable.writeOTAData(ota_address_offset,content + sizeof(CommProtocolOTARequestContent_t),size - sizeof(CommProtocolOTARequestContent_t));
    //         ota_address_offset += size - sizeof(CommProtocolOTARequestContent_t);
    //         response_content->status = ota_writedata_finished;
    //         break;
    //     case comm_protocol_ota_type_stop:
    //         __portable.startOTA();
    //         response_content->status = ota_stop_finished;
    //         break;
    //     default:
    //         response_content->status = ota_response_failed;
    //         break;
    // }
    // __portable.sendResponse(response_buffer,sizeof(CommProtocolResponseHeader_t) + sizeof(CommProtocolOTAResponseContent_t));
}


static void CommProtocol_Default(char *content,int size){

}

int CommProtocol_parseRequest(void *buffer){
    CommProtocolRequestHeader_t *request_header = (CommProtocolRequestHeader_t*)buffer;
    uint8_t *content = ((uint8_t*)buffer) + sizeof(CommProtocolRequestHeader_t);

    //printf("crc16 data %x\r\n",crc16(content,request_header->length));
    
    if ( request_header->magic_number != __magic_number ) return Error_UnknownFormat;
    if ( __certified == 0 && request_header->type != RequestType_Auth ) return Error_Illegal_1;
    if ( __certified != 0 && request_header->type == RequestType_Auth ) return Error_Illegal_2;
    if (__certified != 0 && request_header->auth_code!=__auth_key) return Error_Code;
    if ( request_header->crc16 != crc16(content,request_header->length) ) return Error_CRCFailed;
    switch ( request_header->type ){
        case RequestType_Auth: CommProtocol_auth((char*)content,request_header->length); break;
        case RequestType_HeartBeat: CommProtocol_heartBeat((char*)content,request_header->length); break;
        case RequestType_Read: CommProtocol_read((char*)content,request_header->length); break;
        case RequestType_Write: CommProtocol_write((char*)content,request_header->length); break;
        case RequestType_OTA: CommProtocol_OTA((char*)content,request_header->length); break;
        case RequestType_Default: CommProtocol_Default((char*)content,request_header->length); break;
    }
    return Error_None;
}

void CommProtocol_loop(){
    if(__certified==1){
        if ( __heartbeat_remain > 0 ){
            __heartbeat_remain--;
        }else{
            /* Connection lost. */
            __certified = 0;
        }
    }

}
